package com.xxx.xxx.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

import com.xxx.xxx.R;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

public class HistogramChartView extends View {

    public static final int MODE_BY_DAY = 0;
    public static final int MODE_BY_WEEK = 1;
    public static final int MODE_BY_MONTH = 2;

    private final int[] MONTH_RES_ID = {
            R.string.jan, R.string.feb, R.string.mar,
            R.string.apr, R.string.may, R.string.june,
            R.string.july, R.string.aug, R.string.sept,
            R.string.oct, R.string.nov, R.string.dec};


    private Paint paint;
    //是否是第一次绘制，第一次需要计算view的宽高
    private boolean isFirst = true;
    private int chartHeight;//图形区高度
    private int widthStepPix;//横坐标单位间距
    private int chartCellHeight;//坐标系每一个的高度
    private final int lineCount = 6;//图形默认列数

    //底部时间坐标文字宽高
    private int bottomTitleWidth;
    private int bottomTitleHeight;

    //每一个横坐标（格子）间距代表多少口数
    private int perCellPuffs;

    //触摸
    private float touchDown_x;
    private float touchDown_y;

    private int uiMode = MODE_BY_DAY;//0:按天统计，1：按周统计，2：按月统计
    private boolean showNumber = false;//是否显示数值，默认不显示
    private Calendar mCalender;

    //当前显示的页索引，左滑+1，右滑-1
    private int pageIndex;

    //吸烟口数数据源
    private List<Integer> puffsList;

    //按月模式用变量
    private List<Integer> puffsSumByMonthList;
    //按周模式用变量
    private List<Integer> puffsSumByWeekList;


    public HistogramChartView(Context context) {
        super(context);
        initPaint();
    }

    public HistogramChartView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initPaint();
        initData();
    }

    private void initData() {
        //数据初始化
        mCalender = Calendar.getInstance();
        puffsList = new ArrayList<>();
        //模拟数据初始化
        for (int t = 0; t < 80; t++) {
            puffsList.add(10);
        }
        for (int t = 0; t < 80; t++) {
            puffsList.add(10);
        }
        for (int t = 0; t < 80; t++) {
            puffsList.add(10);
        }

        mCalender.setFirstDayOfWeek(Calendar.MONDAY);

        pageIndex = 0;
        //日历初始化
        if (uiMode == MODE_BY_DAY) {
            perCellPuffs = 20;
            mCalender.add(Calendar.DAY_OF_YEAR, -(lineCount - 1));
        } else if (uiMode == MODE_BY_WEEK) {
            perCellPuffs = 140;
            Calendar calendar = Calendar.getInstance();
            puffsSumByWeekList = new ArrayList<>();
            //先计算第一周
            int sumPuffs = 0;
            int listIndex = 0;
            int thisDayOfThisWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1;
            for (; listIndex < thisDayOfThisWeek; listIndex++) {
                sumPuffs += puffsList.get(listIndex);
            }
            puffsSumByWeekList.add(sumPuffs);
            calendar.add(Calendar.MONTH, thisDayOfThisWeek - 1);
            //计算中间周
            int totalWeeks = (puffsList.size() - thisDayOfThisWeek) / 7/*一周7天*/ + 1/*当前周*/;
            if ((puffsList.size() - thisDayOfThisWeek) % 7 != 0) {
                totalWeeks += 1;
            }
            sumPuffs = 0;
            for (int i = 1; i < totalWeeks - 1; i++) {
                for (int j = 0; j < 7; j++) {
                    sumPuffs += puffsList.get(listIndex++);
                }
                puffsSumByWeekList.add(sumPuffs);
                calendar.add(Calendar.DAY_OF_YEAR, -7);
                sumPuffs = 0;
            }
            //计算最早的那一周
            sumPuffs = 0;
            for (; listIndex < puffsList.size(); listIndex++) {
                sumPuffs += puffsList.get(listIndex);
            }
            puffsSumByWeekList.add(sumPuffs);

            //坐标系用日历置位到默认的最后一页的左边第一列
            mCalender.add(Calendar.DAY_OF_YEAR, -((lineCount - 1) * 7 + thisDayOfThisWeek - 1));
        } else {
            perCellPuffs = 2400;
            puffsSumByMonthList = new ArrayList<>();
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DAY_OF_YEAR, -(puffsList.size() - 1));
            int validDataYear = calendar.get(Calendar.YEAR);
            int validDataMonth = calendar.get(Calendar.MONTH);

            //日历还原到今天
            calendar.add(Calendar.DAY_OF_YEAR, puffsList.size() - 1);
            //先把数据按照月份整理好
            //先计算第一个月
            int sumPuffs = 0;
            int listIndex;
            for (listIndex = 0; listIndex < calendar.get(Calendar.DAY_OF_MONTH)/*today*/; listIndex++) {
                sumPuffs += puffsList.get(listIndex);
            }
            puffsSumByMonthList.add(sumPuffs);
            calendar.add(Calendar.MONTH, -1);
            //计算中间月份
            int totalMonths = (calendar.get(Calendar.YEAR) - validDataYear) * 12 + (calendar.get(Calendar.MONTH) - validDataMonth) + 1;
            sumPuffs = 0;
            for (int i = 1; i < totalMonths; i++) {
                for (int j = 0; j < calendar.getActualMaximum(Calendar.DAY_OF_MONTH); j++) {
                    sumPuffs += puffsList.get(listIndex++);
                }
                puffsSumByMonthList.add(sumPuffs);
                calendar.add(Calendar.MONTH, -1);
                sumPuffs = 0;
            }
            //计算最早的那个月
            for (; listIndex < puffsList.size(); listIndex++) {
                sumPuffs += puffsList.get(listIndex);
            }
            puffsSumByMonthList.add(sumPuffs);

            //坐标系用日历置位到默认的最后一页的左边第一列
            mCalender.add(Calendar.MONTH, -(lineCount - 1));
        }

    }

    private void initPaint() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(0xFF000000);
    }

    @SuppressLint("DefaultLocale")
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (isFirst) {
            isFirst = false;
            //bottom title width = 26~27dp
            //bottom title height = 20dp
            bottomTitleWidth = dp2px(26.5f);
            widthStepPix = (getWidth() - lineCount * bottomTitleWidth) / (lineCount + 1);

            //按周显示下，坐标时间需要分两行写,所以干脆统一留2倍高度20*2
            bottomTitleHeight = dp2px(40f);
            chartHeight = getHeight() - bottomTitleHeight;
            chartCellHeight = chartHeight / 5;

        }

        paint.setTextSize(dp2px(12));

        if (uiMode == MODE_BY_DAY) {//按天模式

            for (int i = 0; i < lineCount; i++) {
                /*
                【第一步】
                画底部的时间坐标
                 */
                canvas.drawText(String.format("%02d", mCalender.get(Calendar.MONTH) + 1) + "/"
                        + String.format("%02d", mCalender.get(Calendar.DAY_OF_MONTH)), (i + 1) * widthStepPix + (i * bottomTitleWidth), chartHeight, paint);
                /*
                【第二步】
                画坐标系参考点
                 */
                int x = widthStepPix * (i + 1) + bottomTitleWidth * i + bottomTitleWidth / 2;
                paint.setStyle(Paint.Style.FILL);
                paint.setStrokeWidth(dp2px(1.5f));
                for (int j = 0; j < 5; j++) {
                    canvas.drawPoint(x, j * chartCellHeight + chartCellHeight / 2f, paint);
                }
                /*
                【第三步】
                画数据，用线条高度来表示吸烟口数，每个格子默认是：20口
                 */
                //X = page * lineCount + ((lineCount - 1) - i);
                int currentIndex = Math.abs(pageIndex) * lineCount + (lineCount - 1) - i;
                if (currentIndex < puffsList.size()) {
                    paint.setStrokeWidth(dp2px(2f));
                    float lineHeight = puffsList.get(currentIndex) / (float) perCellPuffs * chartCellHeight;
                    canvas.drawLine(
                            x,
                            chartHeight - chartCellHeight / 2f,
                            x,
                            chartCellHeight / 2f + (4 * chartCellHeight - lineHeight), paint);
                    //画数字
                    if (showNumber)
                        canvas.drawText(puffsList.get(currentIndex).toString(), x, chartCellHeight / 2f + (4 * chartCellHeight - lineHeight), paint);

                } /*else {
                    //该时间段太早，没有数据，就不绘制数据
                }*/
                mCalender.add(Calendar.DAY_OF_MONTH, 1);
            }
            //绘制完成，需要把日历置位，方便下一次绘制，把时间重新回到左边第一列的时间
            mCalender.add(Calendar.DAY_OF_MONTH, -lineCount);
        } else if (uiMode == MODE_BY_WEEK) {//按周模式

            for (int i = 0; i < lineCount; i++) {
                /*
                【第一步】
                画底部的时间坐标
                分为两行日期和中间一行连接符
                 */
                //第一行画星期一（一个星期的开始）的日期：
                canvas.drawText(String.format("%02d", mCalender.get(Calendar.MONTH) + 1) + "/"
                        + String.format("%02d", mCalender.get(Calendar.DAY_OF_MONTH)), (i + 1) * widthStepPix + (i * bottomTitleWidth), chartHeight, paint);
                //中间画一个“~”符号
                canvas.drawText("~", (i + 1) * widthStepPix + (i * bottomTitleWidth) + bottomTitleWidth / 2f, chartHeight + bottomTitleHeight / 4f, paint);
                //移动日期到星期日
                mCalender.add(Calendar.DAY_OF_YEAR, 6);
                //第二行画星期日（一个星期的结束）的日期：
                canvas.drawText(String.format("%02d", mCalender.get(Calendar.MONTH) + 1) + "/"
                        + String.format("%02d", mCalender.get(Calendar.DAY_OF_MONTH)), (i + 1) * widthStepPix + (i * bottomTitleWidth), chartHeight + bottomTitleHeight / 2f, paint);
                /*
                【第二步】
                画坐标系参考点
                 */
                int x = widthStepPix * (i + 1) + bottomTitleWidth * i + bottomTitleWidth / 2;
                paint.setStyle(Paint.Style.FILL);
                paint.setStrokeWidth(dp2px(1.5f));
                for (int j = 0; j < 5; j++) {
                    canvas.drawPoint(x, j * chartCellHeight + chartCellHeight / 2f, paint);
                }
                /*
                【第三步】
                画数据，用线条高度来表示吸烟口数，每个格子默认是：25*7口
                 */
                int currentIndex = Math.abs(pageIndex) * lineCount + lineCount - i;
                if (currentIndex <= puffsSumByWeekList.size()) {
                    float lineHeight = puffsSumByWeekList.get(currentIndex - 1) / (float) perCellPuffs * chartCellHeight;
                    canvas.drawLine(x, chartHeight - chartCellHeight / 2f, x, chartCellHeight / 2f + (4 * chartCellHeight - lineHeight), paint);
                    //画数字
                    if (showNumber)
                        canvas.drawText(puffsSumByWeekList.get(currentIndex - 1) + "", x, chartCellHeight / 2f + (4 * chartCellHeight - lineHeight), paint);
                }
                //移动日期到下一个星期一
                mCalender.add(Calendar.DAY_OF_YEAR, 1);
            }
            //绘制完了把日历还原回到左上角第一天，方便下次绘制
            mCalender.add(Calendar.DAY_OF_YEAR, -7 * lineCount);

        } else {//按月模式
            for (int i = 0; i < lineCount; i++) {
                /*
                【第一步】
                画底部的时间坐标
                 */
                canvas.drawText(getResources().getString(MONTH_RES_ID[mCalender.get(Calendar.MONTH)]), (i + 1) * widthStepPix + (i * bottomTitleWidth), chartHeight, paint);
                /*
                【第二步】
                画坐标系参考点
                 */
                int x = widthStepPix * (i + 1) + bottomTitleWidth * i + bottomTitleWidth / 2;
                paint.setStyle(Paint.Style.FILL);
                paint.setStrokeWidth(dp2px(1.5f));
                for (int j = 0; j < 5; j++) {
                    canvas.drawPoint(x, j * chartCellHeight + chartCellHeight / 2f, paint);
                }
                /*
                【第三步】
                画数据，用线条高度来表示吸烟口数
                */
                int currentIndex = Math.abs(pageIndex) * lineCount + lineCount - i;
                if (currentIndex <= puffsSumByMonthList.size()) {
                    float lineHeight = puffsSumByMonthList.get(currentIndex - 1) / (float) perCellPuffs * chartCellHeight;
                    canvas.drawLine(x, chartHeight - chartCellHeight / 2f, x, chartCellHeight / 2f + (4 * chartCellHeight - lineHeight), paint);
                    //画数字
                    if (showNumber)
                        canvas.drawText(puffsSumByMonthList.get(currentIndex - 1) + "", x, chartCellHeight / 2f + (4 * chartCellHeight - lineHeight), paint);
                }

                //日历指向下一个月，准备下一次绘制
                mCalender.add(Calendar.MONTH, 1);
            }
            //绘制完了把日历还原回到左上角第一天，方便下次绘制
            mCalender.add(Calendar.MONTH, -lineCount);
        }

    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
        return super.dispatchTouchEvent(event);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            touchDown_x = event.getX();
            touchDown_y = event.getY();
        } else if (action == MotionEvent.ACTION_MOVE) {

        } else if (action == MotionEvent.ACTION_UP) {
            float deltaX = event.getX() - touchDown_x;
            float deltaY = event.getY() - touchDown_y;
            if (Math.abs(deltaX) > widthStepPix && Math.abs(deltaY) < Math.abs(deltaX)) {
                if (deltaX < 0) {//左滑
                    if (pageIndex < 0) {
                        if (uiMode == MODE_BY_DAY) {
                            mCalender.add(Calendar.DAY_OF_MONTH, lineCount);
                        } else if (uiMode == MODE_BY_WEEK) {
                            mCalender.add(Calendar.DAY_OF_MONTH, lineCount * 7);
                        } else {
                            mCalender.add(Calendar.MONTH, lineCount);
                        }
                        pageIndex++;
                    }
                } else {//右滑
                    if (uiMode == MODE_BY_DAY) {
                        mCalender.add(Calendar.DAY_OF_MONTH, -lineCount);
                    } else if (uiMode == MODE_BY_WEEK) {
                        mCalender.add(Calendar.DAY_OF_MONTH, -lineCount * 7);
                    } else {
                        mCalender.add(Calendar.MONTH, -lineCount);
                    }
                    pageIndex--;
                }

                //判断边界，已经显示到今天，不响应继续左滑
                if (pageIndex <= 0) {
                    invalidate();
                }
            } else if (event.getEventTime() - event.getDownTime() <= 100) {
                //视作单击事件
                showNumber = !showNumber;
                invalidate();
            }
        }
        return true;
    }

    private void setDataList(List<Integer> list) {
        this.puffsList = list;
        initData();
        invalidate();
    }

    public void setUiMode(int mode) {
        if (mode != this.uiMode) {
            isFirst = true;
            this.uiMode = mode;
            initData();
            invalidate();
        }
    }

    public int getSmokeCntToday() {
        return puffsList.get(0);
    }

    public int getSmokeCntThisWeek() {
        return puffsSumByWeekList.get(0);
    }

    public int getSmokeCntThisMonth() {
        return puffsSumByMonthList.get(0);
    }

    private int dp2px(float dpValue) {
        float scale = getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale);
    }
}
